
#include <bits/alltypes.h>
#include <string.h>
#include <unistd.h>
#include <hilog/log.h>
#include <thread>
#include "../include/native_module.h"
#include "uart/src/uart_test.c"
#define BUF_SIZE 512

using namespace std;

int g_uart_fd = -1;
static napi_threadsafe_function g_jsCallback = nullptr;

napi_value CallFunction(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];

    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);
    napi_value resource_name;
    napi_create_string_utf8(env, "UAET_Callback", NAPI_AUTO_LENGTH, &resource_name);
    napi_value oncall;
    napi_status status = napi_get_named_property(env, args[0], "oncall", &oncall);
    if (status != napi_ok)
        return oncall;
    napi_value return_val;
    status = napi_call_function(env, args[0], oncall, 1, &resource_name, &return_val);
    return oncall;
}


void UartReceiveThread(int fd) {
    char buffer[BUF_SIZE];
    OH_LOG_INFO(LOG_APP, "the fd of opened file is %{public}d", fd);
    while(1){
        ssize_t bytesRead = read(fd, buffer, sizeof(buffer));
        if(bytesRead > 0){
            // 将数据拷贝到堆内存（防止线程间竞争）
            char* heapData = new char[bytesRead + 1];
            memcpy(heapData, buffer, bytesRead);
            heapData[bytesRead] = '\0';
            OH_LOG_INFO(LOG_APP, "Receive data: %{public}s", heapData);
            //触发JS回调（异步非阻塞）
            napi_call_threadsafe_function(g_jsCallback, heapData, napi_tsfn_blocking);
        }else{
            OH_LOG_INFO(LOG_APP, "Don't receive data");
        }
    }
}
static napi_ref callbackRef = nullptr;
// 定义线程安全函数的数据处理回调
static void CallJs(napi_env env, napi_value jsCallback, void *context, void *data) {
//     if (callbackRef == nullptr) {
//         OH_LOG_INFO(LOG_APP, "Callback reference is null");
//         return;
//     }
    //关键************************************************************************
    napi_status status = napi_get_reference_value(env, callbackRef, &jsCallback);
//     if (status != napi_ok) {
//         OH_LOG_INFO(LOG_APP, "Failed to get callback from reference");
//         return;
//     }
//     // 检查是否为函数
//     napi_valuetype valueType;
//     napi_typeof(env, jsCallback, &valueType);
//     if (valueType != napi_function) {
//         OH_LOG_INFO(LOG_APP, "jsCallback is not a function");
//         return;
//     }
    // 将C字符串转换为JS字符串
    char *msg = static_cast<char *>(data);
    size_t msg_len = strlen(msg);
    napi_value jsMsg;
    napi_create_string_utf8(env, msg, msg_len, &jsMsg);
    OH_LOG_INFO(LOG_APP, "CallJs data: %{public}s", msg);
    // 调用JS回调
    napi_value undefined;
    status = napi_get_undefined(env, &undefined);
    if (status != napi_ok) {
    OH_LOG_INFO(LOG_APP, "ERROR:napi_get_undefined status = %{public}d", status);
    }
    
    napi_value result;
    status = napi_call_function(env, undefined, jsCallback, 1, &jsMsg, &result);
    if(status != napi_ok){
    // 获取并打印 JS 异常信息
        napi_value error;
        napi_get_and_clear_last_exception(env, &error);
        if (error != nullptr) {
            napi_value message;
            napi_get_named_property(env, error, "message", &message);
            char msgBuffer[256];
            size_t copied;
            napi_get_value_string_utf8(env, message, msgBuffer, sizeof(msgBuffer), &copied);
            OH_LOG_INFO(LOG_APP, "JS Exception: %{public}s", msgBuffer);
        }
    } else {
        OH_LOG_INFO(LOG_APP, "CallJs succeeded");
    }
    // 释放堆内存
    free(msg);
}

//js层注册回调的Native方法
napi_value RegisterCallback(napi_env env, napi_callback_info info){
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    // 检查参数是否为函数
    napi_valuetype valuetype;
    napi_typeof(env, args[0], &valuetype);
    if (valuetype != napi_function) {
        napi_throw_type_error(env, nullptr, "Argument must be a function");
        return nullptr;
    }
    // 销毁旧引用（避免内存泄漏）
    if (callbackRef != nullptr) {
        napi_delete_reference(env, callbackRef);
        callbackRef = nullptr;
    }

    // 创建新引用
    napi_status status = napi_create_reference(env, args[0], 1, &callbackRef);
    if (status != napi_ok) {
        napi_throw_error(env, nullptr, "Failed to create reference");
        return nullptr;
    }
    napi_value jsCallback;
    napi_get_cb_info(env, info, nullptr, nullptr, &jsCallback, nullptr);
    
    napi_value resource_name;
    napi_create_string_utf8(env, "UAET_Callback", NAPI_AUTO_LENGTH, &resource_name);
    
    //创建线程安全函数
    status = napi_create_threadsafe_function(env, jsCallback, nullptr, resource_name, 100, 1, nullptr,
                                                         nullptr, nullptr, CallJs, &g_jsCallback);
    if (status != napi_ok) {
        OH_LOG_ERROR(LOG_APP, "Failed to create threadsafe function: %{public}d", status);
    }
    OH_LOG_INFO(LOG_APP, "RegisterCallback ok ");
    return nullptr;
}

// 注销回调方法
napi_value UnregisterCallback(napi_env env, napi_callback_info info) {
    if (g_jsCallback != nullptr) {
        napi_release_threadsafe_function(g_jsCallback, napi_tsfn_abort);
        g_jsCallback = nullptr;
    }
    return nullptr;
}

int uart_init(char *dev) {
    int iRet;
    /* 1. open */
    //     fd = open_port(dev);
    g_uart_fd = open_port(dev);
    if (g_uart_fd < 0) {
        //         OH_LOG_INFO(LOG_INFO, "open device %s\n",dev);
        OH_LOG_ERROR(LOG_APP, "Failed to open devices %{public}s, fd:%{public}d.", dev, g_uart_fd);
        // printf("open %s err!\n", dev);
        return -2;
    }
    OH_LOG_INFO(LOG_APP, " open dev: %{public}s, fd:%{public}d.", dev, g_uart_fd);
    /* 2. setup
     * 115200,8N1
     * RAW mode
     * return data immediately
     */
    iRet = set_opt(g_uart_fd, 115200, 8, 'N', 1);
    if (iRet) {
        // printf("set port err!\n");
        return -3;
    }
    thread(UartReceiveThread, g_uart_fd).detach();
    return 0;
}

/*
 * ./serial_send_recv <dev>
 */
int uart_send(char *string) {
//     int iRet;
    /* 3. write and read */
    write(g_uart_fd, string, 5);
    return 0;
}